/**
// 注意:
// 除非你知道内部行为，或者并未用到第三个参数，否则 lambda 第三个参数不要使用 auto 类型
// 第三个参数的类型将影响模板的生成，不同的类型将生成不同的绘图接口模板函数，
// 也就是 gray08 和 rgb888 类型的 rander 所使用的 rander_wrapper<> 类型，以及各个 draw::xxx 绘图接口是不同的实例
// 除非必要，默认请传入同一个 rander lambda 参数
// 
// 纯 lambda
auto rander     = [](auto x, auto y, inc::rgb color){
    inc::instance_screen0.set_pixel(x, y, color);
};

// 带 wrapper 的 lambda，可以通过 rander_helper 间接设置属性
auto randerx    = xrander(auto x, auto y, inc::rgb color){
    inc::instance_screen0.set_pixel(x, y, color);
};

// rander 也可以，但是操作没有效果。有些操作并不需要设置属性，那么带上属性是多余的
// 所以这里同时支持两种格式的 lambda
inc::rander_helper::set_texture_mode(randerx, inc::texture_mode_t::full);
inc::draw::rectanglehv({ x, y }, w, value, randerx);

// NOTE:
// 这里也存在不太协调的地方，如 interface/xxx.hpp 系列接口一般都是 lambda 定义在传参的位置，
// 如下定义 lambda 的方式都不正确，详情参考该模块 README

inc::icallback<void(*)()> call = [](){
    // foo
};

inc::icallback<void(*)()> call = inc::icallback<void(*)()>([](){
    // foo
});

auto call = inc::icallback<void(*)()>([](){
    // foo
});

do_sth(call);

// lambda 总是匿名类型的，所以它需要以 auto 类型接收，
// 这样定义 lambda 作为参数传入总是正确的（或者说最大化正确）
auto call = [](){
    // bar
};

do_sth(call);

*/
#ifndef xpack_graphic_rander_wrapper
#define xpack_graphic_rander_wrapper
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::graphic_rander_wrapper::inc
#include"define/base_type.hpp"
#include"dumb/lambda_wrapper.hpp"
#include"graphic/color/+.hpp"
#include"macro/xalways_inline.hpp"
#include"macro/xdebug_fail.hpp"
#include"macro/xexport.hpp"
#include"meta/origin_of.hpp"
#pragma pop_macro("xuser")

namespace mixc::graphic_rander_wrapper::origin{
    enum class thickness_mode_t : u08{
        become_wider,
        become_thick,
    };

    enum class texture_mode_t : u08{
        none,
        full,
        grid90,
        linev,
        lineh,
        grid45,
        left_slash,
        right_slash,
        dot,

        max_texture_mode,
    };
}

namespace mixc::graphic_rander_wrapper::inner{
    using namespace origin;

    #define xarg_gen(COLOR)                                                     \
        template<class lambda_t>                                                \
        concept is_ ## COLOR ## _lambda = requires(lambda_t const & lambda){    \
            lambda(0, 0, inc::COLOR{});                                         \
        };
    #include"graphic/color/xgen.foreach.hpp"

    template<class lambda_t>
    concept is_lambda_with_color_param = false
    #define xarg_gen(COLOR)                                                     \
        or is_ ## COLOR ## _lambda<lambda_t>
    #include"graphic/color/xgen.foreach.hpp"
        ; // 需要分号

    template<class lambda_t>
    inline auto get_color_type(){
        if constexpr (not is_lambda_with_color_param<lambda_t>){
            return inc::gray08{};
        }
        // 判断最后一个参数的类型，约定最后一个参数是颜色类型，如 rgb888、gray08 这些
        #define xarg_gen(COLOR)                                             \
        else if constexpr(is_ ## COLOR ## _lambda<lambda_t>){               \
            return inc::COLOR{};                                            \
        }
        #include"graphic/color/xgen.foreach.hpp"
    }

    template<class lambda_t>
    using color_type_of = decltype(get_color_type<lambda_t>());

    // 保持默认值而非 zero 值，因为 rander_helper 依赖默认值
    template<class color_t>
    struct rander_wrapper_data{
        mutable color_t          thickness_color    = {};   // 0x00 黑色
        mutable color_t          texture_color      = {};   // 0x00 黑色
        mutable u16              thickness          = 1;
        mutable thickness_mode_t thickness_mode     = thickness_mode_t::become_thick;
        mutable texture_mode_t   texture_mode       = texture_mode_t::none;
        mutable u16              texture_span       = 3;
    };
}

namespace mixc::graphic_rander_wrapper::origin{
    template<class base_t, class color_t>
    struct rander_wrapper : base_t{
        using base_t::base_t;
        using the_t                                 = rander_wrapper<base_t, color_t>;
        using data_t                                = inner::rander_wrapper_data<color_t>;

        static constexpr bool   im_rander_wrapper   = true;

        constexpr rander_wrapper() : base_t(){}

        // 现在可以外部直接设置 data_t 成员，因为内部可以对 data_t 成员进行属性化包装
        data_t * operator->() const {
            return &m_data;
        }

    private:
        mutable inner::rander_wrapper_data<color_t> m_data  = {};

        void xalways_inline set_pixel(uxx x, uxx y, color_t color) const {
            if constexpr (inner::is_lambda_with_color_param<base_t>){
                base_t::operator()(x, y, color);
            }
            else{
                base_t::operator()(x, y);
            }
        }
    public:
        void xalways_inline operator()(uxx x, uxx y) const {
            this->set_pixel(x, y, m_data.thickness_color);
        }

        void xalways_inline operator()(uxx x, uxx y, color_t color) const {
            if (m_data.thickness_mode == thickness_mode_t::become_thick){
                for(uxx i = 0, step = 0 - uxx(m_data.thickness / 2); i < m_data.thickness; i++, step++){
                    this->set_pixel(x, y + step, color);
                }
            }
            else{
                for(uxx i = 0, step = 0 - uxx(m_data.thickness / 2); i < m_data.thickness; i++, step++){
                    this->set_pixel(x + step, y, color);
                }
            }
        }

        void xalways_inline texturing(uxx x, uxx y, uxx length) const {
            // 使用 ixx 类型作为循环比较的类型，因为 ixx(x), ixx(length) 可能都是负数
            // ixx 类型可以考虑到这种非法负数的边界条件，但实际上传入 lambda 还是使用 uxx 非负整数类型，
            // 这样 lambda 内部只需要比较是不是超过宽高边界就好了
            switch(m_data.texture_mode){
            case texture_mode_t::none:
                break;
            case texture_mode_t::full:
                for(uxx i = 0; ixx(i) < ixx(length); i++){
                    this->set_pixel(x + i, y, m_data.texture_color);
                }
                break;

            case texture_mode_t::grid90:
            case texture_mode_t::linev:
                for(uxx t = x - x % m_data.texture_span,
                    i = t < x ? t + m_data.texture_span : t,
                    end = x + length;
                    ixx(i) < ixx(end);
                    i += m_data.texture_span
                ){
                    this->set_pixel(i, y, m_data.texture_color);
                }

                if (m_data.texture_mode == texture_mode_t::linev){
                    break;
                }
            [[fallthrough]];
            case texture_mode_t::lineh:
                if (y % m_data.texture_span == 0) for(uxx i = 0; ixx(i) < ixx(length); i++){
                    this->set_pixel(x + i, y, m_data.texture_color);
                }
                break;

            case texture_mode_t::grid45:
            case texture_mode_t::left_slash:
                for(uxx t = x - x % m_data.texture_span + y % m_data.texture_span - 1,
                    i = ixx(t) < ixx(x) ? t + m_data.texture_span : t,
                    end = x + length;
                    ixx(i) < ixx(end);
                    i += m_data.texture_span
                ){
                    this->set_pixel(i, y, m_data.texture_color);
                }

                if (m_data.texture_mode == texture_mode_t::left_slash){
                    break;
                }
            [[fallthrough]];
            case texture_mode_t::right_slash:
                for(uxx t = x - x % m_data.texture_span + m_data.texture_span - y % m_data.texture_span - 1,
                    i = ixx(t) < ixx(x) ? t + m_data.texture_span : t,
                    end = x + length;
                    ixx(i) < ixx(end);
                    i += m_data.texture_span
                ){
                    this->set_pixel(i, y, m_data.texture_color);
                }
                break;

            case texture_mode_t::dot:
            default:
                if (y % m_data.texture_span == 0) for(uxx t = x / m_data.texture_span * m_data.texture_span,
                    i = ixx(t) < ixx(x) ? t + m_data.texture_span : t,
                    end = x + length;
                    ixx(i) < ixx(end);
                    i += m_data.texture_span
                ){
                    this->set_pixel(i, y, m_data.texture_color);
                }
                break;
            }
        }
    };

    template<class lambda_t>
    concept is_rander_wrapper = requires(lambda_t lambda, bool value){
        value = lambda.im_rander_wrapper;
    };
}

namespace mixc::graphic_rander_wrapper::inner{
    #define xarg_gen(COLOR)                                                     \
        template<class base_t>                                                  \
        using rander_wrapper_ ## COLOR = origin::rander_wrapper<base_t, inc::COLOR>;
    #include"graphic/color/xgen.foreach.hpp"

    struct sugar_wrapper{
        constexpr sugar_wrapper(){}

        // 返回一个匹配 color 类型的 lambda 包装结构
        // 比如 auto lambda = xrander(uxx x, uxx y, inc::rgb color){ /*...*/ };
        // 通过 xrander 宏扩展，得到一个 rander_wrapper<lambda_type, inc::rgb> 类型的 lambda，
        // 这个包装的 lambda 包含了附加的属性和 operator()() 运算符重载，这里性能作为优先考虑的条件，
        // 在尽可能减少代码膨胀的约束下，使用编译期间生成模板得到各个 draw 系列函数
        template<class lambda_invoke_t>
        auto operator * (lambda_invoke_t && lambda) const {
            // 使用 (lambda_invoke_t &&)lambda 而非 (lambda_t &&)lambda，这个是引用转发的要求
            // (lambda_t &&)lambda 会根据 lambda 实际是左值还是右值进行类型转发一下，而 (lambda_invoke_t &&) 则是纯左值
            using lambda_t = inc::origin_of<lambda_invoke_t>;
            if constexpr (not is_lambda_with_color_param<lambda_t>){
                return inc::lambda_wrapper<rander_wrapper_gray08/*实际上并不需要 color 类型，这里选择一个字节的 gray08*/>() * (lambda_invoke_t &&)lambda;
            }
            // 判断最后一个参数的类型，约定最后一个参数是颜色类型，如 rgb888、gray08 这些
            #define xarg_gen(COLOR)                                             \
            else if constexpr(is_ ## COLOR ## _lambda<lambda_t>){               \
                return inc::lambda_wrapper<                                     \
                    rander_wrapper_ ## COLOR                                    \
                >() * (lambda_invoke_t &&)lambda;                               \
            }
            #include"graphic/color/xgen.foreach.hpp"
        }
    };

    constexpr sugar_wrapper sugar{};
}

#define xranderx ::mixc::graphic_rander_wrapper::inner::sugar *
#define xrander  xranderx[&]

#endif

xexport_space(mixc::graphic_rander_wrapper::origin)
